package site.wlwsjsx.iot_backend.mapper;

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.transaction.annotation.Transactional;
import site.wlwsjsx.iot_backend.dto.LabWorkstationStatsDTO;
import site.wlwsjsx.iot_backend.entity.LabInfo;
import site.wlwsjsx.iot_backend.mapper.LabInfoMapper;

import java.util.List;

import static org.junit.jupiter.api.Assertions.*;

/**
 * LabInfoMapper 接口的完整测试类
 * 覆盖所有方法的测试用例，确保数据库操作正确性
 */
@SpringBootTest
@Transactional // 测试结束后自动回滚，不污染数据库
public class LabInfoMapperTest {

    @Autowired
    private LabInfoMapper labInfoMapper;

    // 测试数据对象
    private LabInfo testLab1;
    private LabInfo testLab2;
    // 测试数据前缀，用于区分真实数据
    private static final String TEST_PREFIX = "TEST-";

    /**
     * 测试前初始化：创建并插入测试数据
     */
    @BeforeEach
    void setUp() {
        // 创建第一个测试实验室
        testLab1 = new LabInfo();
        testLab1.setLabCode(TEST_PREFIX + "LAB001");
        testLab1.setLabName("测试物联网实验室");
        testLab1.setLabLocation("实训楼301室");
        testLab1.setMaxWorkstation(50);
        testLab1.setCurStudentNum(25);
        labInfoMapper.insert(testLab1);
        assertNotNull(testLab1.getId(), "测试实验室1插入后ID不应为null");

        // 创建第二个测试实验室
        testLab2 = new LabInfo();
        testLab2.setLabCode(TEST_PREFIX + "LAB002");
        testLab2.setLabName("测试计算机实验室");
        testLab2.setLabLocation("实训楼302室");
        testLab2.setMaxWorkstation(40);
        testLab2.setCurStudentNum(15);
        labInfoMapper.insert(testLab2);
        assertNotNull(testLab2.getId(), "测试实验室2插入后ID不应为null");
    }

    /**
     * 测试后清理：手动删除测试数据（双重保障）
     */
    @AfterEach
    void tearDown() {
        if (testLab1 != null && testLab1.getId() != null) {
            labInfoMapper.deleteById(testLab1.getId());
        }
        if (testLab2 != null && testLab2.getId() != null) {
            labInfoMapper.deleteById(testLab2.getId());
        }
    }

    /**
     * 测试selectById方法
     */
    @Test
    void testSelectById() {
        // 测试查询存在的实验室
        LabInfo result = labInfoMapper.selectById(testLab1.getId());
        assertNotNull(result, "查询存在的实验室应返回非null结果");
        assertEquals(testLab1.getLabCode(), result.getLabCode(), "实验室编号不匹配");
        assertEquals(testLab1.getLabName(), result.getLabName(), "实验室名称不匹配");
        assertEquals(50, result.getMaxWorkstation(), "总工位数量不匹配");

        // 测试查询不存在的实验室
        LabInfo nonExistResult = labInfoMapper.selectById(-1);
        assertNull(nonExistResult, "查询不存在的实验室应返回null");
    }

    /**
     * 测试selectAll方法
     */
    @Test
    void testSelectAll() {
        List<LabInfo> labList = labInfoMapper.selectAll();
        assertNotNull(labList, "查询结果不应为null");
        assertFalse(labList.isEmpty(), "查询结果不应为空列表");

        // 统计测试数据数量
        long testLabCount = labList.stream()
                .filter(lab -> lab.getLabCode().startsWith(TEST_PREFIX))
                .count();
        assertEquals(2, testLabCount, "查询结果应包含2条测试数据");
    }

    /**
     * 测试insert方法
     */
    @Test
    void testInsert() {
        // 创建新的测试实验室
        LabInfo newLab = new LabInfo();
        newLab.setLabCode(TEST_PREFIX + "LAB003");
        newLab.setLabName("测试电子实验室");
        newLab.setLabLocation("实训楼303室");
        newLab.setMaxWorkstation(30);
        newLab.setCurStudentNum(10);

        // 执行插入操作
        int rowsAffected = labInfoMapper.insert(newLab);
        assertEquals(1, rowsAffected, "插入操作应影响1行数据");
        assertNotNull(newLab.getId(), "插入后ID应自动生成");

        // 验证插入结果
        LabInfo insertedLab = labInfoMapper.selectById(newLab.getId());
        assertNotNull(insertedLab, "应查询到新插入的实验室");
        assertEquals(newLab.getLabCode(), insertedLab.getLabCode(), "插入的实验室编号不匹配");
        assertEquals(30, insertedLab.getMaxWorkstation(), "插入的总工位数量不匹配");

        // 清理新增数据
        labInfoMapper.deleteById(newLab.getId());
    }

    /**
     * 测试update方法
     */
    @Test
    void testUpdate() {
        // 修改测试实验室1的信息
        testLab1.setLabName("更新后的物联网实验室");
        testLab1.setCurStudentNum(30); // 从25更新为30
        testLab1.setMaxWorkstation(55); // 从50更新为55

        // 执行更新操作
        int rowsAffected = labInfoMapper.update(testLab1);
        assertEquals(1, rowsAffected, "更新操作应影响1行数据");

        // 验证更新结果
        LabInfo updatedLab = labInfoMapper.selectById(testLab1.getId());
        assertNotNull(updatedLab, "应查询到更新后的实验室");
        assertEquals("更新后的物联网实验室", updatedLab.getLabName(), "实验室名称更新失败");
        assertEquals(30, updatedLab.getCurStudentNum(), "当前学生数更新失败");
        assertEquals(55, updatedLab.getMaxWorkstation(), "总工位数量更新失败");
    }

    /**
     * 测试deleteById方法
     */
    @Test
    void testDeleteById() {
        // 执行删除操作
        int rowsAffected = labInfoMapper.deleteById(testLab1.getId());
        assertEquals(1, rowsAffected, "删除操作应影响1行数据");

        // 验证删除结果
        LabInfo deletedLab = labInfoMapper.selectById(testLab1.getId());
        assertNull(deletedLab, "删除后不应再查询到该实验室");
    }

    /**
     * 测试selectByPage方法
     */
    @Test
    void testSelectByPage() {
        // 测试第一页数据（偏移量0，每页1条）
        List<LabInfo> firstPage = labInfoMapper.selectByPage(0, 1);
        assertNotNull(firstPage, "第一页查询结果不应为null");
        assertEquals(1, firstPage.size(), "第一页应返回1条数据");

        // 测试第二页数据（偏移量1，每页1条）
        List<LabInfo> secondPage = labInfoMapper.selectByPage(1, 1);
        assertNotNull(secondPage, "第二页查询结果不应为null");
        assertEquals(1, secondPage.size(), "第二页应返回1条数据");

        // 验证分页数据包含测试数据
        long testDataCount = secondPage.stream()
                .filter(lab -> lab.getLabCode().startsWith(TEST_PREFIX))
                .count();
        assertEquals(1, testDataCount, "分页数据应包含测试数据");
    }

    /**
     * 测试countAll方法
     */
    @Test
    void testCountAll() {
        Long totalCount = labInfoMapper.countAll();
        assertNotNull(totalCount, "总数查询结果不应为null");
        assertTrue(totalCount >= 2, "总数应至少包含2条测试数据");
    }

    /**
     * 测试selectIdByLabCode方法
     */
    @Test
    void testSelectIdByLabCode() {
        // 测试查询存在的实验室编号
        Integer labId = labInfoMapper.selectIdByLabCode(testLab1.getLabCode());
        assertNotNull(labId, "查询存在的实验室编号应返回非null结果");
        assertEquals(testLab1.getId(), labId, "查询到的ID与预期不符");

        // 测试查询不存在的实验室编号
        Integer nonExistId = labInfoMapper.selectIdByLabCode(TEST_PREFIX + "NONE");
        assertNull(nonExistId, "查询不存在的实验室编号应返回null");
    }

    /**
     * 测试selectWorkstationStatsByLabId方法
     */
    @Test
    void testSelectWorkstationStatsByLabId() {
        // 测试查询存在的实验室工位统计
        LabWorkstationStatsDTO stats = labInfoMapper.selectWorkstationStatsByLabId(testLab1.getId());
        assertNotNull(stats, "查询存在的实验室应返回工位统计数据");

        // 验证工位统计数据（请根据实际DTO的getter方法名修改）
        // 假设DTO中总工位的方法是getTotalWorkstations()，已用工位的方法是getActiveWorkstations()
        assertEquals(testLab1.getMaxWorkstation(), stats.getTotalWorkstations(), "总工位数量不匹配");
        assertEquals(testLab1.getCurStudentNum(), stats.getActiveWorkstations(), "已用工位数量不匹配");

        // 测试查询不存在的实验室工位统计
        LabWorkstationStatsDTO nonExistStats = labInfoMapper.selectWorkstationStatsByLabId(-1);
        assertNull(nonExistStats, "查询不存在的实验室应返回null");
    }
}
